<?php
namespace app\service;
use EasyWeChat\Foundation\Application;

class WechatTransfer
{
# 微信支付
    public $mchid = '';
    public $appid = '';
    public $serial_no = ''; // 证书序列号，不知道怎么获取的看上面的图
    public $sslKeyPath = ''; // 证书序列号，不知道怎么获取的看上面的图
    public $sslCertPath = ''; // 证书序列号，不知道怎么获取的看上面的图
 
    public function __construct(){
        $this->mchid = getenv('WECHAT_MERCHANTID');
        $this->appid = getenv('WECHAT_APPID2');
        $this->serial_no = getenv('WECHAT_SERIAL_NO'); // 证书序列号，不知道怎么获取的看上面的图
        $this->sslKeyPath = ROOT_PATH.DS.'application'.DS."config".DS."payment_cert".DS."wechatpay".DS."apiclient_key.pem";;
        $this->sslCertPath = ROOT_PATH.DS.'application'.DS."config".DS."payment_cert".DS."wechatpay".DS."apiclient_cert.pem";;
        $this->wechatpayKeyPath = ROOT_PATH.DS.'application'.DS."config".DS."payment_cert".DS."wechatpay".DS."wechatpay_7E4C87ED5C3CC62C4BB0373B7CB81B5EEADBE669.pem";;

    }



    public function transfer($openid = '', $trade_no = '', $money = '', $user_name = '', $desc='备注说明', $remark='备注说明')
    {
        $money = (int)($money * 100);

        $post_data = [
            "appid" => $this->appid,//appid
            "out_batch_no" => $trade_no,//商家批次单号
            "batch_name" => $desc,//批次名称
            "batch_remark" => $desc,//批次备注
            "total_amount" => $money,// 转账金额单位为“分”
            "total_num" => 1, // 转账总笔数
            //此处可以多笔提现  组合二维数组放到transfer_detail_list即可   我这里单笔操作，写死了
            "transfer_detail_list" => [
                [
                    'out_detail_no' => $trade_no,
                    'transfer_amount' => $money,
                    'transfer_remark' => $remark,
                    'openid' => $openid
                ]
            ]
        ];
        if($user_name){
            $post_data['transfer_detail_list'][0]['user_name'] = $this->getEncrypt($user_name);
        }
        $url = 'https://api.mch.weixin.qq.com/v3/transfer/batches';
        //JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE  防止中文被转义
        $result = $this->wx_post($url, json_encode($post_data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));

        $result = json_decode($result, true);
        if (isset($result['prepay_id'])) {
            return $result['prepay_id'];
        }
        return $result;
    }
    public function sendPacket($money, $mch_billno, $openid, $remark = '', $act_name = 'APP提现', $send_name = '那蓝之恋APP', $wishing = '恭喜发财')
    {

        $money = (int)($money * 100);
        $options = [
            'app_id' => getenv('WECHAT_GZH_APPID'),
            // payment
            'payment' => [
                'merchant_id'        => $this->mchid,
                'key'                => getenv('WECHAT_KEY'),
                'cert_path'          => $this->sslCertPath,
                'key_path'           => $this->sslKeyPath,
            ],
        ];

        $app = new Application($options);

        $luckyMoney = $app->lucky_money;

        $luckyMoneyData = [
            'mch_billno'       => $mch_billno,
            'send_name'        => $send_name,
            're_openid'        => $openid,
            'total_num'        => 1,  //固定为1，可不传
            'total_amount'     => $money,  //单位为分，不小于100
            'wishing'          => $wishing,
            'act_name'         => $act_name,
            'remark'           => $remark,
            'scene_id'         => 'PRODUCT_4'
            // ...
        ];

        $result = $luckyMoney->sendNormal($luckyMoneyData);
        return $result;
    }

    public function batchesStatus($batch_id)
    {
        $url = "https://api.mch.weixin.qq.com/v3/transfer/batches/batch-id/$batch_id?need_query_detail=true&detail_status=ALL";
        $authorization = $this->getV3Sign($url, "GET", "");
        $header[]         = 'User-Agent:https://zh.wikipedia.org/wiki/User_agent';
        $header[]         = 'Accept:application/json';
        $header[]         = 'Authorization:' . $authorization;
        $result = $this->http_Request($url, $header);

        $result = json_decode($result, true);
        if($result['transfer_batch']['batch_status'] != 'FINISHED'){
            usleep(500000);
            return $this->batchesStatus($batch_id);
        }
        if ($result['transfer_detail_list'][0]['detail_status'] == 'FAIL') {
            return $this->failDetail($batch_id, $result['transfer_detail_list'][0]['detail_id']);
        }
        return 0;
    }
    public function failDetail($batch_id, $detail_id)
    {
        $reason = [
            "ACCOUNT_FROZEN"=>"账户冻结",
            "REAL_NAME_CHECK_FAIL"=>"用户未实名",
            "NAME_NOT_CORRECT"=>"用户姓名校验失败",
            "OPENID_INVALID"=>"Openid校验失败",
            "TRANSFER_QUOTA_EXCEED"=>"超过用户单笔收款额度",
            "DAY_RECEIVED_QUOTA_EXCEED"=>"超过用户单日收款额度",
            "MONTH_RECEIVED_QUOTA_EXCEED"=>"超过用户单月收款额度",
            "DAY_RECEIVED_COUNT_EXCEED"=>"超过用户单日收款次数",
            "PRODUCT_AUTH_CHECK_FAIL"=>"产品权限校验失败",
            "OVERDUE_CLOSE"=>"转账关闭",
            "ID_CARD_NOT_CORRECT"=>"用户身份证校验失败",
            "ACCOUNT_NOT_EXIST"=>"用户账户不存在",
            "TRANSFER_RISK"=>"转账存在风险",
            "REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED"=>"用户账户收款受限，请引导用户在微信支付查看详情",
            "RECEIVE_ACCOUNT_NOT_PERMMIT"=>"未配置该用户为转账收款人",
            "PAYER_ACCOUNT_ABNORMAL"=>"商户账户付款受限，可前往商户平台-违约记录获取解除功能限制指引",
            "PAYEE_ACCOUNT_ABNORMAL"=>"用户账户收款异常，请引导用户完善其在微信支付的身份信息以继续收款",
            "TRANSFER_REMARK_SET_FAIL"=>"转账备注设置失败，请调整对应文案后重新再试",
        ];
        $url = "https://api.mch.weixin.qq.com/v3/transfer/batches/batch-id/$batch_id/details/detail-id/$detail_id";
        $authorization = $this->getV3Sign($url, "GET", "");
        $header[]         = 'User-Agent:https://zh.wikipedia.org/wiki/User_agent';
        $header[]         = 'Accept:application/json';
        $header[]         = 'Authorization:' . $authorization;
        $result = $this->http_Request($url, $header);

        $result = json_decode($result, true);
        if (isset($result['fail_reason'])) {
            return $reason[$result['fail_reason']];
        }
        return '未知原因';
    }

    public function queryPacket($mchBillNo)
    {
        $options = [
            'app_id' => getenv('WECHAT_GZH_APPID'),
            // payment
            'payment' => [
                'merchant_id'        => $this->mchid,
                'key'                => getenv('WECHAT_KEY'),
                'cert_path'          => $this->sslCertPath,
                'key_path'           => $this->sslKeyPath,
            ],
        ];

        $app = new Application($options);

        $luckyMoney = $app->lucky_money;

        return $luckyMoney->query($mchBillNo);
    }

    /**post请求
     * @param $url
     * @param $param
     * @return bool|string
     */
    private function wx_post($url, $param)
    {
        $authorization = $this->getV3Sign($url, "POST", $param);
        $curl = curl_init();
        $WechatpaySerial = $this->get_Certificates();
        $headers = [
            'Authorization:' . $authorization,
            'Accept:application/json',
            'Content-Type:application/json;charset=utf-8',
            'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
            'Wechatpay-Serial:' . $WechatpaySerial
        ];
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
        curl_setopt($curl, CURLOPT_POST, true);
        $res = curl_exec($curl);
        curl_close($curl);
        return $res;
    }

    /**
     * 微信提现V3签名
     * @param $url
     * @param $http_method
     * @param $body
     * @return string
     */
    private function getV3Sign($url, $http_method, $body)
    {
        $nonce = strtoupper($this->createNonceStr(32));
        $timestamp = time();
        $url_parts = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        //拼接参数
        $message = $http_method . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";
        $private_key = $this->getPrivateKey();
        openssl_sign($message, $raw_sign, $private_key, 'sha256WithRSAEncryption');
        $sign   = base64_encode($raw_sign);
        $token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%s",serial_no="%s",signature="%s"', $this->mchid, $nonce, $timestamp, $this->serial_no, $sign);
        return $token;
    }

    /**
     * 生成随机32位字符串
     * @param $length
     * @return string
     */
    public function createNonceStr($length = 16) { //生成随机16个字符的字符串
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 获取私钥
     * @param $filepath
     * @return false|resource
     */
    private function getPrivateKey()
    {
        return openssl_get_privatekey(file_get_contents($this->sslKeyPath));
    }


    private function getEncrypt($str) {
        //$str是待加密字符串 
        $public_key_path = $this->wechatpayKeyPath;
        $public_key = file_get_contents($public_key_path);
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            //base64编码 
            $sign = base64_encode($encrypted);
        } else {
            throw new Exception('encrypt failed');
        }
        return $sign;
    } 



    /**
     * 获取平台证书内容
     */
    public function get_Certificates()
    {
        $authorization = $this->getV3Sign("https://api.mch.weixin.qq.com/v3/certificates", "GET", "");
        $header[]         = 'User-Agent:https://zh.wikipedia.org/wiki/User_agent';
        $header[]         = 'Accept:application/json';
        $header[]         = 'Authorization:' . $authorization;
        $back=$this->http_Request("https://api.mch.weixin.qq.com/v3/certificates",$header);
        return json_decode($back,true)['data'][0]['serial_no'];
    }
 
    /**
     * 获取sign
     * @param $url
     * @param $http_method [POST GET 必读大写]
     * @param $body [请求报文主体（必须进行json编码）]
     * @param $mch_private_key [商户私钥]
     * @param $merchant_id [商户号]
     * @param $serial_no [证书编号]
     * @return string
     */
    private function get_Sign($url, $http_method, $body, $mch_private_key, $merchant_id, $serial_no)
    {
        $timestamp     = time();//时间戳
        $nonce         = $timestamp . rand(10000, 99999);//随机字符串
        $url_parts     = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $message       =
            $http_method . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";
        openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
        $sign  = base64_encode($raw_sign);
        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $merchant_id, $nonce, $timestamp, $serial_no, $sign);
        return $token;
    }
 
    /**
     * 数据请求
     * @param $url
     * @param array $header 获取头部
     * @param string $post_data POST数据，不填写默认以GET方式请求
     * @return bool|string
     */
    public function http_Request($url, $header = array(), $post_data = "")
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 2);
        if ($post_data != "") {
            curl_setopt($ch, CURLOPT_POST, TRUE);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //设置post提交数据
        }
        //判断当前是不是有post数据的发
        $output = curl_exec($ch);
        if ($output === FALSE) {
            $output = "curl 错误信息: " . curl_error($ch);
        }
        curl_close($ch);
        return $output;
    }
}
?>